package cn.singno.bob.jpa.base.impl;

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.hibernate.SQLQuery;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.transform.Transformers;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

/**
 * <p>名称：CustomRepositoryPlugImpl.java</p>
 * <p>描述：自定义插件超类，提供自定义的基本工具方法</p>
 * <pre>
 *    在使用该类时，业务实现类不要实现业务接口，
 *    业务实现类命名　×PlugImpl　这样扫描器才能找到相应的方法
 *    
 *    
 * </pre>
 * @author 鲍建明
 * @date 2014年8月17日 下午8:51:39
 * @version 1.0.0
 */
public abstract class BasePlugRepository<T, ID> {

	private final static String COUNT = "select count(*) ";

	private final static String FROM = "from"; 
	
	@PersistenceContext
	private EntityManager em;
	
	protected EntityManager getEntityManager(){
		return em;
	}
	
	/******************* execute ********************/
	
	public int execute(String jpql) {
		return this.execute(jpql, null);
	}

	public int execute(String jpql, Object param) {
		return this.execute(jpql, new Object[]{param});
	}

	public int execute(String jpql, Object[] params) {
		return setParam(jpql, params).executeUpdate();
	}
	
	public T update(T entity) {
		return em.merge(entity);
	}

	
	/************************* count *************************/

	public long count(String sql) {
		return this.count(sql, null);
	}

	public long count(String sql, Object[] params) {
		try{
			return Long.valueOf(setParam(sql, params).getSingleResult().toString());
		}catch(NoResultException e){
			return 0;
		}
	}

	public long count(String sql, Object param) {
		return this.count(sql, new Object[]{param});
	}
	
	/*---------------------------find--------------------------------*/	
	

	public Page<T> find(String whereHQL, Object[] params,
			Pageable page) {
		if (page == null) {
			page = new PageRequest(1, 20);
		}
		List<T> list = this.list(whereHQL, params, page.getPageNumber(), page.getPageSize());
		return new PageImpl<T>(list, page, getRowCount(whereHQL, params)); 
	}
	
	public Page<T> find(String whereHQL, Pageable page) {
		return this.find(whereHQL, null, page);
	}
	

	public Page<T> find(String whereHQL, Object param,
			Pageable page) {
		return this.find(whereHQL, new Object[]{param}, page);
	}
	

	/*---------------------------list--------------------------------*/	
	
	public List<T> list(String whereHQL){
		return this.list(whereHQL, null, null, null);
	}
	
	public List<T> list(String whereHQL, Object[] params) {
		return this.list(whereHQL, params,null, null);
	}
	
	public List<T> list(String whereHQL, Pageable page) {
		return this.list(whereHQL, null, page);
	}

	public List<T> list(String whereHQL, Object param, Pageable page) {
		return this.list(whereHQL, new Object[]{param}, page);
	}
	
	public List<T> list(String whereHQL, Object[] params, Pageable page) {
		if (page == null) {
			page = new PageRequest(1, 20);
		}
		return this.list(whereHQL, params, page.getPageNumber(), page.getPageSize());
	}
	
	public List<T> list(String whereHQL, Object param, Integer pageNow,
			Integer pageSize) {
		return this.list(whereHQL, new Object[]{param}, pageNow, pageSize);
	}
	
	
	@SuppressWarnings("unchecked")
	public List<T> list(String whereHQL, Object[] params, Integer pageNow,
			Integer pageSize) {
		return getResult(whereHQL, params, pageNow, pageSize);
	}
	
	/*--------------------------map---------------------------------*/
	
	public Page<Map<String, Object>> findMap(String whereHQL,
			Object[] params, Pageable page) {
		if (page == null) {
			page = new PageRequest(1, 20);
		}
		List<Map<String, Object>> map = this.findMap(whereHQL, params, page.getPageNumber(), page.getPageSize());
		return new PageImpl<Map<String, Object>>(map, page, getRowCount(whereHQL, params)); 
	}

	
	@SuppressWarnings("unchecked")
	protected List<Map<String, Object>> findMap(String whereHQL,
			Object[] params, Integer pageNow, Integer pageSize){
		return getResult(whereHQL, params, pageNow, pageSize);
	}
	
	
	/********************************** native query **********************************************/

	@SuppressWarnings("unchecked")
	public List<T> listNative(String whereHQL, Object[] params,
			Integer pageNow, Integer pageSize) {
		return (List<T>) getNativeResult(whereHQL, params, pageNow, pageSize, Transformers.TO_LIST);
	}

	public List<T> listNative(String whereHQL, Object param, Integer pageNow,
			Integer pageSize) {
		return this.listNative(whereHQL, new Object[]{param}, pageNow, pageSize);
	}

	public Page<T> findNative(String whereHQL, String countHQL, Pageable page) {
		return this.findNative(whereHQL, countHQL, null, page);
	}

	public Page<T> findNative(String whereHQL, String countHQL, Object param,
			Pageable page) {
		return this.findNative(whereHQL, countHQL, new Object[]{param}, page);
	}
	
	public Page<T> findNative(String whereHQL, String countHQL, Object param,
			Integer pageNow, Integer pageSize) {
		return this.findNative(whereHQL, countHQL, new Object[]{param}, pageNow, pageSize);
	}

	public Page<T> findNative(String whereHQL, String countHQL,
			Object[] params, Pageable page) {
		List<T> list = this.listNative(whereHQL, params, page.getOffset(), page.getPageSize());
		return new PageImpl<T>(list, page, this.count(countHQL, params));
	}

	public Page<T> findNative(String whereHQL,  String countHQL,Object[] params,
			Integer pageNow, Integer pageSize) {
		return this.findNative(whereHQL, countHQL, params, new PageRequest(pageNow, pageSize));
	}
	
	@SuppressWarnings("unchecked")
	public Page<Map<String, Object>> findNativeMap(String whereHQL,
			String countHQL, Object[] params, Pageable page) {
		List<Map<String, Object>> list =  (List<Map<String, Object>>) this.getNativeResult(whereHQL, params, page.getOffset(), page.getPageSize(), Transformers.ALIAS_TO_ENTITY_MAP);
		return  new PageImpl<Map<String, Object>>(list, page, this.count(countHQL, params));
	}
	
	
	
	/*********************************** private ********************************/
	
	
	/**
	 * 原生SQL
	 * @param whereHQL
	 * @param params
	 * @param pageNow
	 * @param pageSize
	 * @param transformer 结果的类型
	 * @return
	 */
	private List<?> getNativeResult(String whereHQL, Object[] params, Integer pageNow, Integer pageSize, ResultTransformer transformer){
		Query query = setNativeParam(whereHQL, params);
		query.unwrap(SQLQuery.class).setResultTransformer(transformer);
		if (pageNow == null || pageSize == null) {
			return query.getResultList();
		}
		return query.setFirstResult((pageNow - 1) * pageSize).setMaxResults(pageSize).getResultList();
	}


	
	
	/**
	 * 
	 * <p>描述：原生设置参数</p>
	 * <pre>
	 *    
	 * </pre>
	 * @param jpql
	 * @param params
	 * @return
	 */
	private Query setNativeParam(String jpql, Object[] params){
		Query query = getNativeQuery(jpql);
		this.setParams(query, params);
		return query;
	}
	
	/**
	 * 
	 * <p>描述：设置参数</p>
	 * <pre>
	 *    
	 * </pre>
	 * @param jpql
	 * @param params
	 * @return
	 */
	private Query setParam(String jpql, Object[] params){
		Query query = this.getQuery(jpql);
		this.setParams(query, params);
		return query;
	}
	
	
	/**
	 * 设置HQL语句需要的参数
	 * @param query
	 * @param params
	 */
	private void setParams(Query query, Object[] params) {
		if(params != null && params.length > 0){
			for(int i = 0; i < params.length; i++){
				query.setParameter(i + 1, params[i]);//将Object[] params中的元素传进来,传给对象query中的sql语句中的问号
			}
		}
	}
	
	
	/**
	 * 获取结果
	 * @param whereHQL
	 * @param params
	 * @param pageNow
	 * @param pageSize
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private List getResult(String whereHQL,
			Object[] params, Integer pageNow, Integer pageSize){
		if (pageNow == null || pageSize == null) {
			return setParam(whereHQL, params).getResultList();
		}
		return setParam(whereHQL, params).setFirstResult((pageNow - 1) * pageSize).setMaxResults(pageSize).getResultList();
	} 
	
	/**
	 * 
	 * <p>描述： </p>
	 * <pre>
	 *    
	 * </pre>
	 * @param jpql
	 * @return
	 */
	private long getRowCount(String jpql, Object[] params){				
		StringBuffer sb = new StringBuffer(jpql);
		return this.count( COUNT + sb.substring(sb.indexOf(FROM)) , params);
	}
	
	/******************************* protected *********************************/
	
	/**
	 * 
	 * <p>描述：获取Query对象</p>
	 * <pre>
	 *    
	 * </pre>
	 * @param jpql
	 * @return
	 */
	protected Query getQuery(String jpql){
		return em.createQuery(jpql);
	}
	
	/**
	 * 获取原生的SQL对象
	 * @param jpql
	 * @return
	 */
	protected Query getNativeQuery(String jpql){
		return em.createNativeQuery(jpql);
	}
}
